const E_OPTION = {
    IfMatch: 1,
    UriHost: 3,
    ETag: 4,
    IfNoneMatch: 5,
    UriPort: 7,
    LocationPath: 8,
    UriPath: 11,
    ContentFormat: 12,
    MaxAge: 14,
    UriQuery: 15,
    Accept: 17,
    LocationQuery: 20,
    ProxyUri: 35,
    ProxyScheme: 39,
    Sizel: 60
}

const E_T = {
    CON: 0,
    NON: 1,
    ACK: 2,
    RST: 3
}

class Packet {
    constructor(messageID) {
        this.Ver = 1
        this.T = E_T.CON
        this.TKL = 0
        this.Code = 0
        this.messageID = messageID

        this.options = []
    }

    setVer(Ver) {
        this.Ver = Ver
    }
    getVer() {
        return this.Ver
    }

    setT(T) {
        this.T = T
    }
    getT() {
        return this.T
    }

    setTKL(TKL) {
        this.TKL = TKL
    }
    getTKL() {
        return this.TKL
    }

    setToken(token) {
        this.TKL = token.length
        this.token = token
    }
    getToken() {
        return this.token
    }

    setCode(Code) {
        this.Code = Code
    }
    getCode() {
        return this.Code
    }

    setMessageID(messageID) {
        this.messageID = messageID
    }
    getMessageID() {
        return this.messageID
    }

    addOption(option) {
        this.options.push(option)
    }

    getLength() {
        let length = 4
        this.options.forEach(element => {
            length += element.getLength()
        });
        return length
    }

    writeBuffer() {
        let buffer = Buffer.allocUnsafe(this.getLength())
        buffer.writeUInt8(parseInt(this.getVer() << 6) + parseInt(this.getT() << 4) + parseInt(this.getTKL()), 0)
        buffer.writeUInt8(this.getCode, 1)
        buffer.writeUInt16BE(this.getMessageID(), 2)
        let offset = 4
        this.options.forEach(element => {
            element.writeBuffer(buffer, offset)
            offset += element.getLength()
        })
        return buffer
    }
}

class Option {
    constructor(delta, value) {
        this.delta = delta
        this.value = value
    }

    getLength() {
        if(this.value.length > 0) {
            let length = 0
            if(this.delta < 13) {
                length += 1
            } else if (this.delta < 269) {
                length += 2
            } else {
                length += 3
            }
            if(this.value.length >= 13 && this.value.length < 269) {
                length += 1
            } else if (this.value.length >= 269) {
                length += 2
            }
            length += this.value.length
            return length
        } else {
            return 0
        }
    }

    writeBuffer(buffer, offset) {
        if(this.getLength() > 0) {
            if(this.delta < 13) {
                if(this.value.length < 13) {
                    buffer.writeUInt8(parseInt(this.delta << 4) + parseInt(this.value.length), offset)
                } else if (this.value.length < 269) {
                    buffer.writeUInt8(parseInt(this.delta << 4) + 13, offset)
                } else {
                    buffer.writeUInt8(parseInt(this.delta << 4) + 14, offset)
                }
            } else if (this.delta < 269) {
                if(this.value.length < 13) {
                    buffer.writeUInt8(parseInt(13 << 4) + parseInt(this.value.length), offset)
                } else if (this.value.length < 269) {
                    buffer.writeUInt8(parseInt(13 << 4) + 13, offset)
                } else {
                    buffer.writeUInt8(parseInt(13 << 4) + 14, offset)
                }
            } else {
                if(this.value.length < 13) {
                    buffer.writeUInt8(parseInt(14 << 4) + parseInt(this.value.length), offset)
                } else if (this.value.length < 269) {
                    buffer.writeUInt8(parseInt(14 << 4) + 13, offset)
                } else {
                    buffer.writeUInt8(parseInt(14 << 4) + 14, offset)
                }
            }
            offset += 1
    
            if(this.delta >= 13 && this.delta < 269) {
                buffer.writeUInt8(this.delta - 13, offset)
                offset += 1
            } else if (this.delta >= 269) {
                buffer.writeUInt16BE(this.delta - 269, offset)
                offset += 2
            }
    
            if(this.value.length >= 13 && this.value.length < 269) {
                buffer.writeUInt8(this.value.length - 13, offset)
                offset += 1
            } else if (this.value.length >= 269) {
                buffer.writeUInt16BE(this.value.length - 269, offset)
                offset += 2
            }
    
            buffer.write(this.value, offset)
        }
    }
}

export class CoapClient {
    constructor(host, port) {
        this.host = host
        this.port = port
        this.message_count = 0
        this.udp = wx.createUDPSocket()
        this.udp.bind()

        this.udp.onMessage(msg => {
            console.log(String.fromCharCode.apply(null, new Uint8Array(msg.message)))
        })
    }

    get(resource) {
        this.message_count += 1
        if(this.message_count > 65535) {
            this.message_count = 0
        }
        var packet = new Packet(this.message_count)
        let option = new Option(E_OPTION.UriPath, resource)
        packet.addOption(option)
        let buffer = packet.writeBuffer()
        this.udp.send({
            address: this.host,
            port: this.port,
            message: buffer.buffer
        })
    }

}
